"""
语音生成代理 - 使用 DashScope API (推荐)
"""

import os
import re
import time
from pathlib import Path
from typing import Dict, List

from mm_story_agent.base import register_tool


class CosyVoiceSynthesizer:
    """使用 DashScope SDK 的语音合成器"""

    def __init__(self, api_key=None):
        """
        初始化语音合成器
        api_key: DashScope API Key (不是 access_key_id)
        """
        self.api_key = api_key or os.getenv('DASHSCOPE_API_KEY') or ''

        if not self.api_key:
            raise ValueError(
                "缺少必要的 DASHSCOPE_API_KEY\n"
                "请通过以下方式之一设置:\n"
                "1. 在配置文件中设置 api_key\n"
                "2. 设置环境变量 DASHSCOPE_API_KEY\n"
                "3. 运行 setup_env.ps1"
            )

        # 导入 dashscope SDK
        try:
            import dashscope
            from dashscope.audio.tts_v2 import SpeechSynthesizer as DSSynthesizer

            dashscope.api_key = self.api_key
            self.DSSynthesizer = DSSynthesizer
            print("✅ DashScope SDK 初始化成功")
        except ImportError:
            raise ImportError(
                "请安装 dashscope SDK:\n"
                "pip install dashscope"
            )

    def call(self, save_file, transcript, voice="longxiaochun_v2", sample_rate=16000):
        """
        生成语音并保存

        参数:
            save_file: 保存路径
            transcript: 要合成的文本
            voice: 音色名称 (默认: longxiaochun_v2)
            sample_rate: 采样率 (DashScope 输出固定为 22050Hz)
        """

        try:
            # 创建合成器实例 - 使用 cosyvoice-v2
            synthesizer = self.DSSynthesizer(
                model='cosyvoice-v2',
                voice=voice
            )

            # 调用合成
            audio_data = synthesizer.call(transcript)

            # 保存音频
            with open(save_file, 'wb') as f:
                f.write(audio_data)

            # 验证文件大小
            file_size = os.path.getsize(save_file)
            if file_size < 100:
                raise RuntimeError(f"生成的文件太小: {file_size} bytes")

            return True

        except Exception as e:
            error_msg = str(e)

            # 常见错误提示
            if "Invalid API-key" in error_msg or "InvalidApiKey" in error_msg:
                raise RuntimeError(
                    "API Key 无效,请检查:\n"
                    "1. API Key 是否正确\n"
                    "2. 是否已开通语音合成服务\n"
                    "3. 是否使用了正确的 DashScope API Key (不是 access_key_id)"
                )
            elif "voice" in error_msg.lower() or "model" in error_msg.lower():
                raise RuntimeError(
                    f"音色或模型错误: {error_msg}\n"
                    "推荐使用的音色:\n"
                    "- longxiaochun_v2 (女声)\n"
                    "- longwan_v2 (女声)\n"
                    "- longyue_v2 (女声)\n"
                    "- longdeng_v2 (男声)"
                )
            else:
                raise RuntimeError(f"语音合成失败: {error_msg}")


def split_text_for_tts(text: str, max_chars: int) -> List[str]:
    """将长文本拆分为适合 TTS 的片段"""
    if not text:
        return []

    cleaned = re.sub(r'\s+', ' ', text.strip())
    if not cleaned:
        return []

    sentence_pattern = r'(?<=[。！？!?；;…]|[\.!?])\s+'
    sentences = re.split(sentence_pattern, cleaned)
    sentences = [s.strip() for s in sentences if s.strip()]

    if not sentences:
        sentences = [cleaned]

    chunks: List[str] = []
    current = ""

    def flush_current():
        nonlocal current
        if current.strip():
            chunks.append(current.strip())
        current = ""

    for sentence in sentences:
        if len(sentence) > max_chars:
            flush_current()
            for start in range(0, len(sentence), max_chars):
                part = sentence[start:start + max_chars].strip()
                if part:
                    chunks.append(part)
            continue

        candidate = f"{current} {sentence}".strip() if current else sentence
        if len(candidate) <= max_chars:
            current = candidate
        else:
            flush_current()
            current = sentence

    flush_current()

    return chunks or [cleaned[:max_chars]]


def files_are_valid(files: List[Path], min_size: int = 1000) -> bool:
    if not files:
        return False
    return all(f.exists() and os.path.getsize(f) >= min_size for f in files)


@register_tool("cosyvoice_tts")
class CosyVoiceAgent:
    """CosyVoice TTS Agent - 使用 DashScope API"""

    def __init__(self, cfg) -> None:
        self.cfg = cfg

    def call(self, params: Dict):
        pages: List = params["pages"]
        save_path: Path = params["save_path"]

        api_key = params.get("api_key") or os.getenv('DASHSCOPE_API_KEY')

        if not api_key:
            print("\n" + "="*60)
            print("⚠️  语音生成配置错误")
            print("="*60)
            print("请在配置文件的 speech_generation.params 中添加:")
            print('  api_key: "sk-your-dashscope-api-key"')
            print("\n或者设置环境变量:")
            print("  $env:DASHSCOPE_API_KEY='sk-your-key'  # PowerShell")
            print("="*60 + "\n")
            raise ValueError("缺少 DASHSCOPE_API_KEY")

        try:
            generation_agent = CosyVoiceSynthesizer(api_key=api_key)
        except Exception as e:
            print(f"❌ 初始化失败: {e}")
            raise

        voice = params.get("voice", "longxiaochun_v2")
        sample_rate = self.cfg.get("sample_rate", 16000)
        max_chars = max(150, int(params.get("max_chars_per_request", 400)))
        request_interval = float(params.get("request_interval", 0.6))
        max_retries = max(1, int(params.get("max_retries", 3)))

        print(f"\n{'='*60}")
        print("🎙️ 语音生成 (DashScope API)")
        print(f"  模型: cosyvoice-v2")
        print(f"  音色: {voice}")
        print(f"  最大单次字符数: {max_chars}")
        print(f"  输出采样率: 22050 Hz (DashScope 固定)")
        print(f"{'='*60}\n")

        success_count = 0
        from tqdm import tqdm

        for idx, page in enumerate(tqdm(pages, desc="生成语音"), 1):
            page_text = str(page).strip()
            if not page_text:
                tqdm.write(f"  ⚠️ 页面 {idx}: 文本为空，跳过")
                continue

            try:
                single_file = save_path / f"p{idx}.wav"
                chunk_files = sorted(save_path.glob(f"p{idx}_*.wav"))

                if files_are_valid([single_file]):
                    tqdm.write(f"  ℹ️ 页面 {idx}: 检测到现有语音，跳过")
                    success_count += 1
                    continue

                if files_are_valid(chunk_files):
                    tqdm.write(f"  ℹ️ 页面 {idx}: 检测到 {len(chunk_files)} 个语音片段，跳过")
                    success_count += 1
                    continue

                if single_file.exists():
                    single_file.unlink()
                for f in chunk_files:
                    if f.exists():
                        f.unlink()

                chunks = split_text_for_tts(page_text, max_chars=max_chars)
                total_chunks = len(chunks)
                page_success = True

                for chunk_idx, chunk_text in enumerate(chunks, 1):
                    if total_chunks == 1:
                        target_file = save_path / f"p{idx}.wav"
                    else:
                        target_file = save_path / f"p{idx}_{chunk_idx:03d}.wav"

                    chunk_success = False

                    for attempt in range(1, max_retries + 1):
                        try:
                            generation_agent.call(
                                save_file=target_file,
                                transcript=chunk_text,
                                voice=voice,
                                sample_rate=sample_rate
                            )

                            size = os.path.getsize(target_file)
                            if size < 1000:
                                raise RuntimeError(f"生成的文件太小 ({size} bytes)")

                            tqdm.write(
                                f"  ✅ 页面 {idx} 片段 {chunk_idx}/{total_chunks}: 成功 ({size:,} bytes)"
                            )
                            chunk_success = True
                            break

                        except Exception as e:
                            if target_file.exists() and os.path.getsize(target_file) < 1000:
                                target_file.unlink()

                            if attempt < max_retries:
                                wait_time = request_interval * attempt
                                tqdm.write(
                                    f"  ⚠️ 页面 {idx} 片段 {chunk_idx}/{total_chunks}: 第 {attempt} 次失败，"
                                    f"{wait_time:.1f} 秒后重试 ({str(e)[:60]})"
                                )
                                time.sleep(wait_time)
                            else:
                                tqdm.write(
                                    f"  ❌ 页面 {idx} 片段 {chunk_idx}/{total_chunks}: 重试失败 ({str(e)[:80]})"
                                )

                    if not chunk_success:
                        page_success = False
                        break

                    time.sleep(request_interval)

                if page_success:
                    success_count += 1
                else:
                    tqdm.write(f"  ❌ 页面 {idx}: 有片段生成失败，已跳过")

            except Exception as e:
                tqdm.write(f"  ❌ 页面 {idx}: {str(e)[:80]}")
                continue

        print(f"\n{'='*60}")
        print(f"✅ 完成: {success_count}/{len(pages)}")
        print(f"{'='*60}\n")

        return {
            "modality": "speech",
            "success_count": success_count,
            "total_count": len(pages)
        }